﻿using System;
using System.ComponentModel.DataAnnotations;
using System.ServiceModel.DomainServices.Client;
using System.ServiceModel.DomainServices.Client.ApplicationServices;
using Falafel.Services.Web.Resources;

namespace Falafel.Services.Web
{
    /// <summary>
    /// Extensions to provide client side custom validation and data binding to <see cref="RegistrationData"/>.
    /// </summary>
    public partial class RegistrationData
    {
        private OperationBase _currentOperation;

        /// <summary>
        /// Gets or sets a function that returns the password.
        /// </summary>
        [Display(AutoGenerateField=false)]
        public Func<string> PasswordAccessor { get; set; }

        /// <summary>
        /// Gets and sets the password.
        /// </summary>
        [Required(ErrorMessageResourceName = "ValidationErrorRequiredField", ErrorMessageResourceType = typeof(ErrorResources))]
        [Display(Order = 3, Name = "PasswordLabel", Description = "PasswordDescription", ResourceType = typeof(RegistrationDataResources))]
        [RegularExpression("^.*[^a-zA-Z0-9].*$", ErrorMessageResourceName = "ValidationErrorBadPasswordStrength", ErrorMessageResourceType = typeof(ErrorResources))]
        [StringLength(50, MinimumLength = 7, ErrorMessageResourceName = "ValidationErrorBadPasswordLength", ErrorMessageResourceType = typeof(ErrorResources))]
        public string Password
        {
            get
            {
                return (PasswordAccessor == null) ? string.Empty : PasswordAccessor();
            }

            set
            {
                ValidateProperty("Password", value);
                CheckPasswordConfirmation();

                // Do not store the password in a private field as it should
                // not be stored in memory in plain-text.  Instead, the supplied
                // PasswordAccessor serves as the backing store for the value.

                RaisePropertyChanged("Password");
            }
        }

        /// <summary>
        /// Gets or sets a function that returns the password confirmation.
        /// </summary>
        [Display(AutoGenerateField=false)]
        public Func<string> PasswordConfirmationAccessor { get; set; }

        /// <summary>
        /// Gets and sets the password confirmation string.
        /// </summary>
        [Required(ErrorMessageResourceName = "ValidationErrorRequiredField", ErrorMessageResourceType = typeof(ErrorResources))]
        [Display(Order = 4, Name = "PasswordConfirmationLabel", ResourceType = typeof(RegistrationDataResources))]
        public string PasswordConfirmation
        {
            get
            {
                return (PasswordConfirmationAccessor == null) ? string.Empty : PasswordConfirmationAccessor();
            }

            set
            {
                ValidateProperty("PasswordConfirmation", value);
                CheckPasswordConfirmation();

                // Do not store the password in a private field as it should
                // not be stored in memory in plain-text.  Instead, the supplied
                // PasswordAccessor serves as the backing store for the value.

                RaisePropertyChanged("PasswordConfirmation");
            }
        }

        /// <summary>
        /// Gets or sets the current registration or login operation.
        /// </summary>
        [Display(AutoGenerateField=false)]
        public OperationBase CurrentOperation
        {
            get
            {
                return _currentOperation;
            }
            set
            {
                if (_currentOperation != value)
                {
                    if (_currentOperation != null)
                    {
                        _currentOperation.Completed -= (s, e) => CurrentOperationChanged();
                    }

                    _currentOperation = value;

                    if (_currentOperation != null)
                    {
                        _currentOperation.Completed += (s, e) => CurrentOperationChanged();
                    }

                    CurrentOperationChanged();
                }
            }
        }

        /// <summary>
        /// Gets a value indicating whether the user is presently being registered or logged in.
        /// </summary>
        [Display(AutoGenerateField = false)]
        public bool IsRegistering
        {
            get
            {
                return CurrentOperation != null && !CurrentOperation.IsComplete;
            }
        }

        /// <summary>
        /// Helper method for when the current operation changes.
        /// Used to raise appropriate property change notifications.
        /// </summary>
        private void CurrentOperationChanged()
        {
            RaisePropertyChanged("IsRegistering");
        }

        /// <summary>
        /// Checks to ensure the password and confirmation match.  If they don't match,
        /// then a validation error is added.
        /// </summary>
        private void CheckPasswordConfirmation()
        {
            // If either of the passwords has not yet been entered, then don't test for equality between
            // the fields.  The Required attribute will ensure a value has been entered for both fields.
            if (string.IsNullOrWhiteSpace(Password)
                || string.IsNullOrWhiteSpace(PasswordConfirmation))
            {
                return;
            }

            // If the values are different, then add a validation error with both members specified
            if (Password != PasswordConfirmation)
            {
                ValidationErrors.Add(new ValidationResult(ErrorResources.ValidationErrorPasswordConfirmationMismatch, new [] { "PasswordConfirmation", "Password" }));
            }
        }

        /// <summary>
        /// Perform logic after the UserName value has been entered
        /// </summary>
        /// <param name="userName">The user name value that was entered.</param>
        /// <remarks>
        /// Allow the form to indicate when the value has been completely entered, because
        /// using the OnUserNameChanged method can lead to a premature call before the user
        /// has finished entering the value in the form.
        /// </remarks>
        
        public void UserNameEntered(string userName)
        {
            // Auto-Fill FriendlyName to match UserName for new entities when there is not a friendly name specified
            if ((EntityState == EntityState.New || EntityState == EntityState.Detached)
                && string.IsNullOrWhiteSpace(FriendlyName))
            {
                FriendlyName = userName;
            }
        }

        /// <summary>
        /// Creates a new <see cref="LoginParameters"/>
        /// initialized with this entity's data (IsPersistent will default to false)
        /// </summary>
        public LoginParameters ToLoginParameters()
        {
            return new LoginParameters(UserName, Password, false, null);
        }
    }
}
